{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial 11: Inflows\n",
    "\n",
    "This tutorial walks you through the process of introducing inflows of vehicles into a network. Inflows allow us to simulate open networks where vehicles may enter (and potentially exit) the network consanstly, such as a section of a highway or of an intersection.\n",
    "\n",
    "The rest of this tutorial is organized as follows: \n",
    "\n",
    "- In [**section 1**](#1.-Creating-inflows-in-Flow), we introduce inflows and show how to create them into Flow.\n",
    "- In [**section 2**](#2.-Running-simulations-with-inflows), we simulate the merge network in the presence of inflows.\n",
    "- In [**section 3**](#3.-Customizing-inflows), we explain the different options you have to customize inflows."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Creating inflows in Flow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this tutorial, we will simulate inflows through a highway network with an entrance ramp (an on-merge). As we will see, the perturbations caused by the vehicles entering through the ramp leads to the formation of congested waves downstream in the main highway.\n",
    "\n",
    "We begin by importing the merge network class provided by Flow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.networks import MergeNetwork"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A schematic of the above network is displayed in the figure below. As we can see, the edges at the start of the main highway and of the on-merge are named `inflow_highway` and `inflow_merge` respectively. These names will be important when we begin creating our inflows, as we will need to specify by which edges the vehicles should enter the network."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"img/merge_scheme.png\" width=\"750\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We also need to define the types of the vehicles that are placed in the network through our inflows. These types are string values that allow us to distinguish between vehicles. For instance, we could have two types of vehicles entering through the main highway, one for human-driven vehicles and one for RL-driven vehicles.\n",
    "\n",
    "For this tutorial, we will only use one type of vehicles, with the vehicle identifier `human`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.core.params import VehicleParams\n",
    "from flow.controllers import IDMController\n",
    "from flow.core.params import SumoCarFollowingParams\n",
    "\n",
    "# create an empty vehicles object\n",
    "vehicles = VehicleParams()\n",
    "\n",
    "# add some vehicles to this object of type \"human\"\n",
    "vehicles.add(\"human\",\n",
    "             acceleration_controller=(IDMController, {}),\n",
    "             car_following_params=SumoCarFollowingParams(\n",
    "                 speed_mode=\"obey_safe_speed\",  \n",
    "                 # we use the speed mode \"obey_safe_speed\" for better dynamics at the merge\n",
    "             ),\n",
    "             num_vehicles=20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have created a new type of vehicle, called `human`, and we directly inserted 20 vehicles of this type into the network. These vehicles will already be on the network when the simulation starts, contrary to the vehicles added by the inflow which will only start coming in the network after the simulation starts.\n",
    "\n",
    "Note that it is not necessary to add vehicles at the start. If you don't wish that to happen, you can set `num_vehicles=0`, which is the default value if you don't specify `num_vehicles` at all."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we are ready to import and create an empty `InFlows` object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.core.params import InFlows\n",
    "\n",
    "inflow = InFlows()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `InFlows` object is provided as an input during the network creation process via the `NetParams` parameter. Introducing these inflows into the network is handled by the backend network generation processes during instantiation of the network object.\n",
    "\n",
    "In order to add new inflows of vehicles of pre-defined types onto specific edges and lanes in the network, we use the `InFlows` object's `add` method. This function accepts the following parameters (more will be shown in section 3):\n",
    "\n",
    "* `veh_type`: the type of the vehicles the inflow will create (this must match one of the types set in the `VehicleParams` object),\n",
    "* `edge`: the name of the edge (in the network) where the inflow will insert vehicles,\n",
    "* `vehs_per_hour`: the maximum number of vehicles entering from the edge per hour (this number may not be achievable due to congestion and safe driving behavior).\n",
    "\n",
    "More options are shown in [**section 3**](#3.-Customizing-inflows).\n",
    "\n",
    "We begin by creating an inflow of vehicles at a rate of 2000 vehicles per hour on the main highway:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inflow.add(veh_type=\"human\",\n",
    "           edge=\"inflow_highway\",\n",
    "           vehs_per_hour=2000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we create a second inflow of vehicles on the inflow_merge lane at a lower rate of 100 vehicles per hour."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inflow.add(veh_type=\"human\",\n",
    "           edge=\"inflow_merge\",\n",
    "           vehs_per_hour=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the next section, we will add our inflows to our network and run a simulation to see them in action."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Running simulations with inflows"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We are now ready to test our inflows in a simulation. Introducing these inflows into the network is handled by the backend network generation processes during the instantiation of the network object. To make this work, the `InFlows` object should be given as a parameter to the `NetParams` object, in addition to all other network-specific parameters.\n",
    "\n",
    "For the merge network, this is done as follows: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.networks.merge import ADDITIONAL_NET_PARAMS\n",
    "from flow.core.params import NetParams\n",
    "\n",
    "additional_net_params = ADDITIONAL_NET_PARAMS.copy()\n",
    "# make the part of the highway after the merge longer\n",
    "additional_net_params['post_merge_length'] = 350  \n",
    "# make the number of lanes on the highway be just one\n",
    "additional_net_params['highway_lanes'] = 1\n",
    "\n",
    "net_params = NetParams(inflows=inflow,  # our inflows\n",
    "                       additional_params=additional_net_params)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we create and start the simulation, following what is explained in tutorial 1.\n",
    "\n",
    "- _If the simulation in SUMO is going too fast, you can slow it down by sliding the \"Delay\" cursor from left to right._\n",
    "- _Don't worry about potential warnings that might come up in the log while runing the simulation._"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.core.params import SumoParams, EnvParams, InitialConfig\n",
    "from flow.envs.ring.accel import AccelEnv, ADDITIONAL_ENV_PARAMS\n",
    "from flow.core.experiment import Experiment\n",
    "\n",
    "sim_params = SumoParams(render=True,\n",
    "                         sim_step=0.2)\n",
    "\n",
    "env_params = EnvParams(additional_params=ADDITIONAL_ENV_PARAMS)\n",
    "\n",
    "initial_config = InitialConfig()\n",
    "\n",
    "flow_params = dict(\n",
    "    exp_tag='merge-example',\n",
    "    env_name=AccelEnv,\n",
    "    network=MergeNetwork,\n",
    "    simulator='traci',\n",
    "    sim=sim_params,\n",
    "    env=env_params,\n",
    "    net=net_params,\n",
    "    veh=vehicles,\n",
    "    initial=initial_config,\n",
    ")\n",
    "\n",
    "# number of time steps\n",
    "flow_params['env'].horizon = 10000\n",
    "exp = Experiment(flow_params)\n",
    "\n",
    "# run the sumo simulation\n",
    "_ = exp.run(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"img/merge_visual.png\" width=\"100%\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Running this simulation, we can see that a large number of vehicles are entering from the main highway, while only a sparse number of vehicles are entering from the on-merge, as we specified in the inflows. Feel free to try different `vehs_per_hour` values so as to have different inflow rates.\n",
    "\n",
    "In the next section, we will see how to exploit the full capabilities of inflows."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Customizing inflows"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you run the previous simulation carefully, you will see that the vehicles entering the network start with no speed. Besides, if you replace `additional_net_params['highway_lanes'] = 1` by `additional_net_params['highway_lanes'] = 2` in [**section 1**](#1.-Creating-inflows-in-Flow), thus making the highway two-lane-wide, you will see that vehicles only enter on the right lane. \n",
    "\n",
    "In this section, we will see how to solve these issues, and how to customize inflows."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We saw that you can create an inflow by doing the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inflow.add(veh_type=\"human\",\n",
    "           edge=\"inflow_highway\",\n",
    "           vehs_per_hour=2000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, this `add` method has a lot more parameters, which we will talk about now."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's start with parameters that allow you to specify the inflow rate, i.e. how many vehicles the inflow will add into the network. \n",
    "\n",
    "There are 3 parameters to do this: \n",
    "\n",
    "- `vehs_per_hour`: we have seen this one before, this is the number of vehicles that should enter the network, in vehicles per hour, equally spaced. For example, as there are $60 \\times 60 = 3600$ seconds in one hour, setting this parameter to $\\frac{3600}{5}=720$ will result in vehicles entering the network every $5$ seconds.\n",
    "\n",
    "- `probability`: this is the probability (between 0 and 1) of a vehicle entering the network every second. For example, if we set this to $0.2$, then at each second of the simulation, a vehicle will enter the network with probability $\\frac{1}{5}$.\n",
    "\n",
    "- `period`: this is the time in seconds between two vehicles are inserted. For example, setting this to $5$ would result in vehicles entering the network every $5$ seconds (which is effectively the same as setting `vehs_per_hour` to $720$).\n",
    "\n",
    "_Note that all these rates are **maximum** rates, meaning that if adding vehicles at the current rate would result in vehicles being too close to each other or colliding, then the rate will automatically be reduced._\n",
    "\n",
    "Exactly **one** of these 3 parameters should be set, no more nor less. You can choose how you would rather have your vehicles enter the network. With `vehs_per_hour` and `period` (which are proportional to each other, use whichever is more convenient to define), vehicles will enter the network equally spaced, while the vehicles will be more randomly separated if you use `probability`.\n",
    "\n",
    "---\n",
    "\n",
    "Now let's look into where and how fast vehicles enter the network.\n",
    "\n",
    "There are 2 parameters taking care of this:\n",
    "\n",
    "- `depart_lane`: this parameter lets you specify in which lane vehicles are inserted when they enter the network on an edge consisting of several lanes. It should be a positive `int`, 0 being the rightmost lane. However most of the time, you don't want vehicles entering through only one lane (although you could create one inflow for each lane). That's why there are other options for this parameter, which are the following strings:\n",
    "\n",
    "    - `\"random\"`: vehicles will enter on a random lane\n",
    "    - `\"free\"`: vehicles will enter on the least occupied lane\n",
    "    - `\"best\"`: vehicles will enter on the `\"free\"` lane among those which allow the vehicle the longest ride without needing to change lane\n",
    "    - `\"first\"`: vehicles will enter on the rightmost lane they can use\n",
    "    \n",
    "  By default, `depart_lane` is set to `\"free\"`, which is why vehicles were only using the rightmost lane on the highway, if several lanes were available.\n",
    "  \n",
    "\n",
    "- `depart_speed`: this parameter lets you specify the speed at which the vehicles will enter the network. It should be a positive `float`, in meters per second. If this speed is unsafe, the departure of the vehicles is delayed. Just like for `depart_lane`, there are other options for this parameter, which are the following strings:\n",
    "\n",
    "    - `\"random\"`: vehicles enter the edge with a random speed between 0 and the speed limit on the edge. The entering speed may be adapted to ensure that a safe distance to the leading vehicle is kept\n",
    "    - `\"max\"`: vehicle speeds at insertion will be adjusted to the maximum safe speed that allows insertion at the specified time to succeed\n",
    "\n",
    "   \n",
    "By default, `depart_speed` is set to 0.\n",
    "\n",
    "---\n",
    "\n",
    "Finally, let's look at the rest of the parameters available:\n",
    "\n",
    "- `name` (`str`): a name for the inflow, which will also be used as a prefix for the ids of the vehicles created by it . This is set to `\"flow\"` by default.\n",
    "\n",
    "\n",
    "- `begin` (`float`): the time of the simulation, in seconds, at which the inflow should start producing vehicles. This is set to 1 second by default, which is the minimum value (setting it to 0 could cause collisions with vehicles that are manually added into the network). \n",
    "\n",
    "\n",
    "- `end` (`float`): the time of the simulation, in seconds, at which the inflow should stop producing vehicles. This is set to 24 hours (86400 seconds) by default.\n",
    "\n",
    "\n",
    "- `number` (`int`): the number of vehicles that should be procuded by the inflow. This is set to `None` by default, which make the inflow keep producing vehicles indefinitely until `end` is reached. If this parameter is specified, the `end` parameter won't be used. Note that if this number is small, it might not be enforced accurately due to rounding up.\n",
    "\n",
    "\n",
    "- `kwargs` (`dict`): you can specify additional parameters if you need to. These can include, for instance, a specific route for the vehicles to follow, an arrival speed, an arrival lane, or even a color for the vehicles, etc. For more information on all the available parameters, and more details on the existing parameters, see [here](https://sumo.dlr.de/wiki/Definition_of_Vehicles,_Vehicle_Types,_and_Routes#Vehicles_and_Routes).\n",
    "\n",
    "---\n",
    "\n",
    "Let us finish this section with a more complex example. This is what we want:\n",
    "\n",
    "- We will use the merge network, with no vehicles being manually pre-inserted into the network.\n",
    "\n",
    "- There will be 4 lanes on the main highway and 2 on the on-merge.\n",
    "\n",
    "- (1) Every hour, 10000 vehicles will enter the highway at maximum speed on a random lane, from the start of the simulation up until the end. These vehicles should be colored in white\n",
    "\n",
    "- (2) Every two seconds, a vehicle will enter the on-merge with no speed, on the right lane, from the start of the simulation up until the end. These vehicles should be colored in green.\n",
    "\n",
    "- (3) Every second, a vehicle should enter with probability 0.1 on the left lane of the on-merge, with random speed. These vehicles should only start entering the network after the first minute of simulation time, and there should be at most 30 of them throughout the whole simulation. These vehicles should be colored in red.\n",
    "\n",
    "_Note: for the colors, you will need to use the `kwargs` parameter._\n",
    "_Also, set_ `color_vehicles` _to_ `False` _in the simulation parameters so that the vehicles are not colored automatically according to their types._"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result should look something like this:\n",
    "\n",
    "<img src=\"img/complex_merge_visual.png\" width=\"100%\"/>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can try to do it yourself as an exercise if you want.\n",
    "\n",
    "Here is a solution code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from flow.core.experiment import Experiment\n",
    "from flow.core.params import NetParams, EnvParams, InitialConfig, InFlows, \\\n",
    "                             VehicleParams, SumoParams, SumoCarFollowingParams\n",
    "from flow.controllers import IDMController\n",
    "from flow.networks import MergeNetwork\n",
    "from flow.networks.merge import ADDITIONAL_NET_PARAMS\n",
    "from flow.envs.ring.accel import AccelEnv, ADDITIONAL_ENV_PARAMS\n",
    "\n",
    "\n",
    "\n",
    "# create a vehicle type\n",
    "vehicles = VehicleParams()\n",
    "vehicles.add(\"human\",\n",
    "             acceleration_controller=(IDMController, {}),\n",
    "             car_following_params=SumoCarFollowingParams(\n",
    "                 speed_mode=\"obey_safe_speed\"))\n",
    "\n",
    "\n",
    "# create the inflows\n",
    "inflows = InFlows()\n",
    "\n",
    "# inflow for (1)\n",
    "inflows.add(veh_type=\"human\",\n",
    "            edge=\"inflow_highway\",\n",
    "            vehs_per_hour=10000,\n",
    "            depart_lane=\"random\",\n",
    "            depart_speed=\"random\",\n",
    "            color=\"white\")\n",
    "\n",
    "# inflow for (2)\n",
    "inflows.add(veh_type=\"human\",\n",
    "            edge=\"inflow_merge\",\n",
    "            period=2,\n",
    "            depart_lane=0,  # right lane\n",
    "            depart_speed=0,\n",
    "            color=\"green\")\n",
    "\n",
    "# inflow for (3)\n",
    "inflows.add(veh_type=\"human\",\n",
    "           edge=\"inflow_merge\",\n",
    "           probability=0.1,\n",
    "           depart_lane=1,  # left lane\n",
    "           depart_speed=\"max\",\n",
    "           begin=60,  # 1 minute\n",
    "           number=30,\n",
    "           color=\"red\")\n",
    "\n",
    "\n",
    "# modify the network accordingly to instructions\n",
    "# (the available parameters can be found in flow/networks/merge.py)\n",
    "additional_net_params = ADDITIONAL_NET_PARAMS.copy()\n",
    "additional_net_params['post_merge_length'] = 350  # this is just for visuals\n",
    "additional_net_params['highway_lanes'] = 4\n",
    "additional_net_params['merge_lanes'] = 2\n",
    "\n",
    "\n",
    "# setup and run the simulation\n",
    "net_params = NetParams(inflows=inflows,\n",
    "                       additional_params=additional_net_params)\n",
    "\n",
    "sim_params = SumoParams(render=True,\n",
    "                         sim_step=0.2)\n",
    "sim_params.color_vehicles = False\n",
    "\n",
    "env_params = EnvParams(additional_params=ADDITIONAL_ENV_PARAMS)\n",
    "\n",
    "initial_config = InitialConfig()\n",
    "\n",
    "flow_params = dict(\n",
    "    exp_tag='merge-example',\n",
    "    env_name=AccelEnv,\n",
    "    network=MergeNetwork,\n",
    "    simulator='traci',\n",
    "    sim=sim_params,\n",
    "    env=env_params,\n",
    "    net=net_params,\n",
    "    veh=vehicles,\n",
    "    initial=initial_config,\n",
    ")\n",
    "\n",
    "# number of time steps\n",
    "flow_params['env'].horizon = 10000\n",
    "exp = Experiment(flow_params)\n",
    "\n",
    "# run the sumo simulation\n",
    "_ = exp.run(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
